package net.cyue.easyconfiguration;

import net.cyue.easyconfiguration.node.NodeType;
import net.cyue.easyconfiguration.node.annotation.*;
import net.cyue.easyconfiguration.node.data.NodeMetaData;
import net.cyue.easyconfiguration.node.definition.AbstractNodeDefinition;
import net.cyue.easyconfiguration.node.definition.TableNodeDefinition;
import net.cyue.easyconfiguration.node.util.NodeDefinitionFactor;
import net.cyue.util.ReflectUtil;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 配置定义
 */
public class ConfigurationDefinition {
    private final Set<AbstractNodeDefinition> definitionsSet = new HashSet<>();

    public void addConfigItem(AbstractNodeDefinition definition) {
        this.definitionsSet.add(definition);
    }
    public Set<AbstractNodeDefinition> getConfigItemDefinitionSet() {
        return this.definitionsSet;
    }

    public static class DefinitionBuilder {

        private final ConfigurationDefinition definition;

        public DefinitionBuilder() {
            this.definition = new ConfigurationDefinition();
        }

        public ConfigurationDefinition build() {
            return this.definition;
        }

        public DefinitionBuilder addConfigItem(
            NodeType type,
            String name,
            String... paths
        ) {
            this.definition.addConfigItem(
                NodeDefinitionFactor.create(null, type, name, paths)
            );
            return this;
        }
        public DefinitionBuilder addConfigItem(
            AbstractNodeDefinition definition
        ) {
            this.definition.addConfigItem(definition);
            return this;
        }


        public DefinitionBuilder addConfigItem(
            String name,
            Class<?> clz,
            Map<String, String> fieldNameMap,
            Map<String, String[]> fieldPathsMap,
            Map<String, NodeType> fieldTypeMap,
            Set<String> ignoreField,
            String... paths
        ) {
            if (fieldNameMap == null) {
                fieldNameMap = new HashMap<>();
            }
            if (fieldPathsMap == null) {
                fieldPathsMap = new HashMap<>();
            }
            if (fieldTypeMap == null) {
                fieldTypeMap = new HashMap<>();
            }

            TableNodeDefinition definition = new TableNodeDefinition(null, name, paths);
            Field[] fields = ReflectUtil.getAllFields(clz);
            for (Field f : fields) {
                NodeMetaData fieldNodeMetaData = NodeMetaData.fromField(f);
                AbstractNodeDefinition fieldDefinition = NodeDefinitionFactor.create(fieldNodeMetaData);
                if (fieldDefinition == null || fieldDefinition.isIgnore()) {
                    continue;
                }

                String fieldName = fieldNameMap.getOrDefault(
                    f.getName(),
                    fieldDefinition.getName()
                );
                String[] fieldPaths = fieldPathsMap.getOrDefault(
                    f.getName(),
                    fieldDefinition.getFullPaths()
                );
                NodeType fieldType = fieldTypeMap.getOrDefault(
                    f.getName(),
                    fieldDefinition.getType()
                );
                fieldDefinition.setName(fieldName);
                fieldDefinition.setPaths(fieldPaths);
                fieldDefinition.setType(fieldType);

                definition.addFieldDefinition(
                    f.getName(),
                    fieldDefinition
                );
            }
            return this.addConfigItem(definition);
        }
        public DefinitionBuilder addConfigItem(
            String name,
            Class<?> clz,
            String... paths
        ) {
            return this.addConfigItem(
                name,
                clz,
                new HashMap<>(),
                new HashMap<>(),
                new HashMap<>(),
                new HashSet<>(),
                paths
            );
        }
        public DefinitionBuilder addConfigItem(
            String name,
            Class<?> clz,
            Set<String> ignoreField,
            String... paths
        ) {
            return this.addConfigItem(
                name,
                clz,
                new HashMap<>(),
                new HashMap<>(),
                new HashMap<>(),
                ignoreField,
                paths
            );
        }
        public DefinitionBuilder addConfigItem(
            Class<?> clz,
            Set<String> ignoreField,
            String... paths
        ) {
            ClassNode classNode = clz.getAnnotation(ClassNode.class);
            return this.addConfigItem(
                classNode == null ? clz.getSimpleName() : classNode.name(),
                clz,
                new HashMap<>(),
                new HashMap<>(),
                new HashMap<>(),
                ignoreField,
                paths
            );
        }
        public DefinitionBuilder addConfigItem(
            Class<?> clz,
            String... paths
        ) {
            return this.addConfigItem(clz, new HashSet<>(), paths);
        }
    }
    public static DefinitionBuilder builder() {
        return new DefinitionBuilder();
    }
}
